package com.electronic.utils;


import com.electronic.appearance.AbstractAppearance;
import com.electronic.domain.EncryptionAlgorithmConstants;
import com.electronic.domain.SealSize;
import com.electronic.enums.AlgorithmEnum;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Objects;
/**
 * IFCAUtil 工具类
 */
public class IFCAUtil {

    /**
     * 将给定的矩形基于页面大小进行转换
     *
     * @param pagesize 页面大小
     * @param rect     待转换的矩形
     * @return 转换后的矩形
     * @throws PdfException 如果矩形为空，抛出此异常
     */
    public static Rectangle convertRectangle(PageSize pagesize, Rectangle rect) throws PdfException {
        // 如果矩形为空，抛出异常
        if (Objects.isNull(rect)) {
            throw new PdfException("Rectangle is null!");
        }

        float x = rect.getX();
        float y = rect.getY();
        float width = rect.getWidth();
        float height = rect.getHeight();
        float pageHeight = pagesize.getHeight();
        // 计算转换后的 Y 坐标
        float newY = pageHeight - y - height;

        return new Rectangle(x, newY, width, height);
    }

    /**
     * @param sealImg  印章图片
     * @param maxValue 允许最大宽高
     * @return java.lang.String 解析后图片尺寸
     */
    public static SealSize calculateSealSize(byte[] sealImg, float maxValue) {
        ImageData imageData = ImageDataFactory.create(sealImg);
        float width = imageData.getWidth();
        float height = imageData.getHeight();

        width = Math.min(width, maxValue);
        height = Math.min(height, maxValue);

        SealSize sealImgSize = new SealSize();
        sealImgSize.setWidth(width);
        sealImgSize.setHeight(height);
        return sealImgSize;
    }

    /**
     * @param sealImg  印章图片
     * @param widthPx  标准宽度 0代表使用默认宽度
     * @param heightPx 标准高度 0代表使用默认高度
     * @return java.lang.String 解析后图片尺寸
     */
    public static SealSize calculateSealSize(byte[] sealImg, int widthPx, int heightPx) {
        ImageData imageData = ImageDataFactory.create(sealImg);
        float width = imageData.getWidth();
        float height = imageData.getHeight();
        if (widthPx != 0) {
            width = widthPx;
        }
        if (heightPx != 0) {
            height = heightPx;
        }

        SealSize sealImgSize = new SealSize();
        sealImgSize.setWidth(width);
        sealImgSize.setHeight(height);
        return sealImgSize;
    }

    /**
     * 缩放图片
     *
     * @param source       原始图片
     * @param formatName   图像格式（例如 "jpg", "png"）
     * @param scaledWidth  目标宽度（像素）
     * @param scaledHeight 目标高度（像素）
     * @throws IOException 如果读取或写入文件时发生错误
     */
    public static byte[] resize(byte[] source, String formatName, int scaledWidth, int scaledHeight) throws IOException {
        BufferedImage originalImage=ImageIO.read(new ByteArrayInputStream(source));
        BufferedImage scaledImage = new BufferedImage(scaledWidth, scaledHeight, originalImage.getType());
        Graphics2D g2d = scaledImage.createGraphics();
        // 设置高质量插值算法
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        // 绘制缩小后的图像
        g2d.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
        g2d.dispose();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(scaledImage, formatName, baos);
        baos.flush();
        byte[] imageInByte = baos.toByteArray();
        baos.close();
        return imageInByte;
    }

    /**
     * 获取私钥
     *
     * @param encodedKey encoded according to the PKCS #8 standard
     * @return
     */
    public static PrivateKey getPrivateKey(byte[] encodedKey) {
        PrivateKey privateKey = null;
        try {
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
            KeyFactory keyFactory;
            keyFactory = KeyFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
            privateKey = keyFactory.generatePrivate(keySpec);
            return privateKey;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 读取X.509证书
     *
     * @param cer 证书
     * @return
     * @throws CertificateException
     * @throws NoSuchProviderException
     */
    public static X509Certificate readX509Certificate(byte[] cer)
            throws CertificateException, NoSuchProviderException {
        CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
        X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(cer));
        return cert;
    }

    /**
     * 获取证书签名算法
     *
     * @param certificate 证书
     */
    public static AlgorithmEnum getCertAlgorithEnum(Certificate certificate) {
        // 获取签名算法的 oid
        String signatureAlgorithmOid = certificate.getPublicKey().getAlgorithm();
        // 判断是否为 SM2 证书
        if (EncryptionAlgorithmConstants.ALGORITHM_EC.equalsIgnoreCase(signatureAlgorithmOid) || EncryptionAlgorithmConstants.ALGORITHM_SM2.equalsIgnoreCase(signatureAlgorithmOid)) {
            return AlgorithmEnum.SM3;
        } else if (EncryptionAlgorithmConstants.ALGORITHM_RSA.equalsIgnoreCase(signatureAlgorithmOid)) {
            return AlgorithmEnum.SHA256;
        }
        return null;
    }

    /**
     * 判断PDF文件中是否有旋转页，如果有则重设显示区域
     *
     * @param pdfDoc     pdf文档对象
     * @param appearance 签名域外观
     * @return 转换后的文档中的矩形
     */
    public static AbstractAppearance rotateRectangle(PdfDocument pdfDoc, AbstractAppearance appearance) {
        //得到当前页对象
        PdfPage pdfPage = pdfDoc.getPage(appearance.getPageNum());
        //获取页面的尺寸(如果页面有旋转的情况下，这种获取方式还是按照默认未旋转返回页面宽高)
        Rectangle pageSize = pdfPage.getPageSize();
        //获取页面的尺寸(如果页面有旋转的情况下，这种获取方式返回旋转后的页面宽高)
        //PageSize pageSize = new PageSize(pdfPage.getPageSizeWithRotation());
        //页面宽
        float pageWidth = pageSize.getWidth();
        //页面高
        float pageHeight = pageSize.getHeight();
        //logger.error("当前页：{}，宽度：{}，高度：{}", appearance.getPageNum(), pageWidth, pageHeight);
        //签名位置
        Rectangle rectangle = appearance.getRectangle();
        Rectangle rectView = new Rectangle(rectangle.getX(), rectangle.getY(), rectangle.getWidth(), rectangle.getHeight());
        //logger.error("当前页：{}，原始X：{}，原始Y：{}，宽度：{}，高度：{}", appearance.getPageNum(), rectView.getX(), rectView.getY(), rectView.getWidth(), rectView.getHeight());
        //如果位置超过了页面的宽度，则进行修正(例如同一份PDF中存在A4页面和A3页面)
        if (rectView.getRight() > pageWidth) {
            float differ = rectView.getRight() - pageWidth;
            rectView.setX(rectView.getX() - differ);
            //logger.error("当前页：{}，修订X：{}，修订Y：{}，宽度：{}，高度：{}", appearance.getPageNum(), rectView.getX(), rectView.getY(), rectView.getWidth(), rectView.getHeight());
        }
        //如果位置超过了页面的高度(签名位置超出了页面顶部)，则进行修正
        if (rectView.getTop() > pageHeight) {
            float differ = rectView.getTop() - pageHeight;
            rectView.setY(rectView.getY() - differ);
            //logger.error("当前页：{}，修订X：{}，修订Y：{}，宽度：{}，高度：{}", appearance.getPageNum(), rectView.getX(), rectView.getY(), rectView.getWidth(), rectView.getHeight());
        }
        //如果位置超过了页面的高度(签名位置超出了页面底部)，则进行修正
        if (rectView.getBottom() > pageHeight) {
            float differ = rectView.getBottom() - pageHeight;
            rectView.setY(rectView.getY() - differ);
            //logger.error("当前页：{}，修订X：{}，修订Y：{}，宽度：{}，高度：{}", appearance.getPageNum(), rectView.getX(), rectView.getY(), rectView.getWidth(), rectView.getHeight());
        }

        //获取页面的旋转角度
        int rotation = pdfPage.getRotation();
        //对旋转角度进行处理，确保在 0 到 359 之间
        rotation %= 360;
        //如果旋转角度为90度或者270度，则重设剪裁框的大小，否则印章不会显示出来
        if (90 == rotation || 270 == rotation) {
            //裁剪框(页面显示的区域)
            Rectangle viewRectangle = pdfPage.getCropBox();
            float viewWidth = viewRectangle.getWidth();
            float viewHeight = viewRectangle.getHeight();
            //重设剪裁框的宽高
            viewRectangle.setWidth(viewHeight);
            viewRectangle.setHeight(viewWidth);
            pdfPage.setCropBox(viewRectangle);
        }
        appearance.setRectangle(rectView);
        return appearance;
    }

    /**
     * 将给定的矩形基于页面大小进行转换(左上角未原点)
     *
     * @param pagesize 页面大小
     * @param rect     待转换的矩形
     * @return 转换后的矩形
     * @throws PdfException 如果矩形为空，抛出此异常
     */
    public static Rectangle convertRectangleToTop(PdfPage pdfPage, PageSize pagesize, Rectangle rect) throws PdfException {
        // 如果矩形为空，抛出异常
        if (Objects.isNull(rect)) {
            throw new PdfException("Rectangle is null!");
        }

        float x = rect.getX();
        float y = rect.getY();
        float width = rect.getWidth();
        float height = rect.getHeight();
        //注意：如果有页面旋转，这里获取到旋转后的高度，非未旋转的高度
        float pageHeight = pagesize.getHeight();
        // 计算转换后的 Y 坐标
        float newY = pageHeight - y - height;

        // 获取页面的旋转角度
        int rotation = pdfPage.getRotation();
        if (90 == rotation || 270 == rotation) {
            newY = pagesize.getWidth() - y - height;
        }

        //logger.error("计算前坐标[{},{}]<------->计算后坐标[{},{}]", x, y, x, newY);
        return new Rectangle(x, newY, width, height);
    }
}
